package meshClipper;

import hEdge.HEFace;
import hEdge.HEGraph;
import hEdge.HENode;
import hEdge.HEdge;
import hdgeom.primitives.Box;
import hdgeom.primitives.Rect;

import java.util.ArrayList;
import java.util.Vector;

import topoMesh.TopoMesh;
import topoMesh.TopoMeshEdge;
import topoMesh.TopoMeshFace;
import topoMesh.TopoMeshNode;
import peasy.*;
import processing.core.PApplet;
import processing.core.PVector;
import processing.pdf.PGraphicsPDF;
import processinghelper.PGraphicsHelper;
import controlP5.*;

public class MeshClipper extends PApplet {
	PeasyCam cam;
	ControlP5 cp5;
	String filePath="/Users/dillenburger_b/Desktop/clipping/input/";
	String fileName="inho20cs2.obj";
	 /* float sX1 = 0;
	  float sY1 = -0.83f;
	  float sZ1 = -.29f;
	  float sX2 = 0.17f;
	  float sY2 = -0.45f;
	  float sZ2 = 0.25f;*/
	  
	/*  float sX1 = -.36f;
	    float sY1 = -0.13f;
	    float sZ1 = -.28f;
	    float sX2 = 0.82f;
	    float sY2 = 0.21f;
	    float sZ2 = 0.25f;*/
	    
	    float sX1 = -.1f;
	    float sY1 = -.4f;
	    float sZ1 = -.3f;
	    float sX2 = 0.7f;
	    float sY2 = 0.6f;
	    float sZ2 = 0.6f;
	boolean doExport;
	boolean doClip;
	
	TopoMesh meshSource;
	TopoMesh meshWork;
	TopoMesh meshExport;
	
	TopoMesh[]meshSides;
	Box bounds;
	Box boundsClip;
	CheckBox checkbox;
	boolean[]corners=new boolean[8];
	PVector[] cornerPoints;
	
	public void setup() {
		size(800, 600, P3D);
		setupGui();
		setupData();
	}
	
	public void setupGui(){
		cp5 = new ControlP5(this);
		cp5.addSlider("sX1").setPosition(20, 20).setRange(-2, 2);
		cp5.addSlider("sX2").setPosition(20, 40).setRange(-2, 2);
		cp5.addSlider("sY1").setPosition(20, 60).setRange(-2, 2);
		cp5.addSlider("sY2").setPosition(20, 80).setRange(-2, 2);
		cp5.addSlider("sZ1").setPosition(20, 100).setRange(-2, 2);
		cp5.addSlider("sZ2").setPosition(20, 120).setRange(-2, 2);
		checkbox = cp5.addCheckBox("checkBox")
                .setPosition(20, 160)
                .setSize(10, 10)
                .setItemsPerRow(2)
                .setSpacingColumn(40)
                .setSpacingRow(20)
                .addItem("x1y1z1", 0)
                .addItem("x2y1z1", 50)
                .addItem("x2y2z1", 100)
                .addItem("x1y2z1", 150)
                .addItem("x1y1z2", 0)
                .addItem("x2y1z2", 50)
                .addItem("x2y2z2", 100)
                .addItem("x1y2z2", 150)
                ;
		cp5.setAutoDraw(false);
		cam = new PeasyCam(this, 300);
	}
	public void setupData(){
		meshSource=new TopoMesh(filePath+fileName);
		meshSource.removeUnusedAndDuplicates();
		meshSource.triangulate();
		meshSource.construct();
		bounds=meshSource.getBounds();
		
		meshWork=new TopoMesh();
		meshWork=meshSource.getCopy();
		meshSides=new TopoMesh[6];
		println(bounds);
	}
	public void draw() {
		for (int i=0;i<8;i++){
			corners[i]=checkbox.getState(i);
		}
		cornerPoints=new PVector[8];
		cornerPoints[0]=new PVector(sX1,sY1,sZ1);
		cornerPoints[1]=new PVector(sX2,sY1,sZ1);
		cornerPoints[2]=new PVector(sX2,sY2,sZ1);
		cornerPoints[3]=new PVector(sX1,sY2,sZ1);
		cornerPoints[4]=new PVector(sX1,sY1,sZ2);
		cornerPoints[5]=new PVector(sX2,sY1,sZ2);
		cornerPoints[6]=new PVector(sX2,sY2,sZ2);
		cornerPoints[7]=new PVector(sX1,sY2,sZ2);
		boundsClip=new Box(sX1,sY1,sZ1,sX2,sY2,sZ2);

		if (doExport) {
			doExport();
			doExport=false;
		}
		if (doClip){
			clip();
			removeBorderFaces();
			println("clip ended");
		}
		// we stop camera while mouse over sliders
		if (mouseX < 200) {
			cam.setActive(false);
		} else {
			cam.setActive(true);
		}
		display();
	}
	public void display(){
		hint(ENABLE_DEPTH_TEST);
		background(0);
		
		//lights();
		noStroke();
		float scale=200;
		translate(-bounds.getCenterX()*scale,-bounds.getCenterY()*scale,-bounds.getCenterZ()*scale);
		scale(scale);
		strokeWeight(1f);
		pointLight(220,220,210,bounds.x1,bounds.y1,bounds.z1);
		pointLight(210,220,220,bounds.x2,bounds.y2,bounds.z2);
		PGraphicsHelper.drawWorldAxis3D(g, 5);
		fill(0,255,0);
		
		// sides
		stroke(0,255,0);
		for (int i=0;i<meshSides.length;i++){
		if (meshSides[i] != null) {
			fill(255);
			stroke(0, 0, 255);
			fill(0, 255, 0);
			meshSides[i].display3D(g);
		}
		}
		// all Faces
		fill(0,255,0);
		noStroke();
		int step=4;
		for (int i=0;i<meshSource.getNFaces();i++){
			TopoMeshFace face=meshSource.getFace(i);
			boolean inside=false;
			for (int j=0;j<face.getNNodes();j++){
				TopoMeshNode n=face.getNode(j);
				if (boundsClip.contains(n.x, n.y, n.z)){
					inside=true;
					break;
				}
			}
			if (inside){
				fill(0,255,0);
				face.display3D(g);
			}
			else if (i%step==0){
				fill(255);
				face.display3D(g);
			}
		}
		
		// corners
		noFill();
		stroke(255,0,0);
		boundsClip.display3D(g);
		noStroke();
		fill(0,255,255);
		for (int i=0;i<corners.length;i++){
			if (corners[i]){
				PVector corner=cornerPoints[i];
				pushMatrix();
				translate(corner.x,corner.y,corner.z);
				box(0.02f);
				popMatrix();
			}
			
		}
		g.fill(0,0,255);
		if (meshExport!=null){
			meshExport.display3D(g);
		}
		
		// change the drawing mode so we can draw the GUI in 2D
		hint(DISABLE_DEPTH_TEST);
		cam.beginHUD();
		noLights();
		cp5.draw();
		cam.endHUD();
	}
	
	public void clip(){
		println("start clip");
		
		//clip in z
		meshWork.construct();
		meshSides[0]=meshWork.trimMeshGetBorderGraph(sZ1,true);
		closeSide(meshSides[0],sX1,sY1,sX2,sY2,sZ1);
		println("end clip sZ1");
		meshWork=meshSource.getCopy();
		meshWork.construct();
		meshSides[1]=meshWork.trimMeshGetBorderGraph(sZ2,true);
		closeSide(meshSides[1],sX1,sY1,sX2,sY2,sZ2);
		println("end clip sZ2");
		//clip in X
		meshWork=meshSource.getCopy();
		meshWork.construct();
		meshWork.XToZ();
		meshSides[2]=meshWork.trimMeshGetBorderGraph(sX1,true);
		closeSide(meshSides[2],sZ1,sY1,sZ2,sY2,sX1);
		meshSides[2].XToZ();
		println("end clip sX1");
		meshWork=meshSource.getCopy();
		meshWork.construct();
		meshWork.XToZ();
		meshSides[3]=meshWork.trimMeshGetBorderGraph(sX2,true);
		closeSide(meshSides[3],sZ1,sY1,sZ2,sY2,sX2);
		meshSides[3].XToZ();
		println("end clip sX2");
		//clip in Y
		meshWork=meshSource.getCopy();
		meshWork.construct();
		meshWork.YToZ();
		meshSides[4]=meshWork.trimMeshGetBorderGraph(sY1,true);
		closeSide(meshSides[4],sX1,sZ1,sX2,sZ2,sY1);
		meshSides[4].YToZ();
		println("end clip sY1");
		meshWork=meshSource.getCopy();
		meshWork.construct();
		meshWork.YToZ();
		meshSides[5]=meshWork.trimMeshGetBorderGraph(sY2,true);
		closeSide(meshSides[5],sX1,sZ1,sX2,sZ2,sY2);
		meshSides[5].YToZ();
		meshWork=meshSource.getCopy();
		doClip=false;
		println("end clip sY2");
	}
	public void doExport(){
		meshExport=meshSource.getCopy();
		meshExport.construct();
		meshExport.triangulate();
		meshExport.trimMeshGetBorderGraph(sZ1,true);
		meshExport.construct();
		meshExport.triangulate();
		meshExport.trimMeshGetBorderGraph(sZ2,false);
		meshExport.construct();
		meshExport.triangulate();
		
		meshExport.XToZ();
		meshExport.trimMeshGetBorderGraph(sX1,true);
		meshExport.construct();
		meshExport.triangulate();
		meshExport.trimMeshGetBorderGraph(sX2,false);
		meshExport.construct();
		meshExport.triangulate();
		meshExport.XToZ();
		
		meshExport.YToZ();
		meshExport.trimMeshGetBorderGraph(sY1,true);
		meshExport.construct();
		meshExport.triangulate();
		meshExport.trimMeshGetBorderGraph(sY2,false);
		meshExport.construct();
		meshExport.triangulate();
		meshExport.YToZ();
		for (int i=0;i<meshSides.length;i++){
			meshExport.faces.addAll(meshSides[i].faces);
			meshExport.nodes.addAll(meshSides[i].nodes);
		}
		meshExport.saveMesh(filePath+fileName+PGraphicsHelper.timestamp(this)+".obj");
		doExport=false;
		println("exported");
	}
	public void removeBorderFaces(){
		for (int i=0;i<meshSides.length;i++){
			TopoMesh mesh=meshSides[i];
			ArrayList<TopoMeshFace>newFaces=new ArrayList<TopoMeshFace>();
			for (int j=0;j<mesh.getNFaces();j++){
				TopoMeshFace fc=mesh.getFace(j);
				boolean isBorder=false;
				for (int jj=0;jj<fc.getNNodes();jj++){
					TopoMeshNode n=fc.getNode(jj);
					for (int jjj=0;jjj<8;jjj++){
						if (corners[jjj]&&n.equalPosition(cornerPoints[jjj])){
							isBorder=true;
							break;
						}
					}
					if (isBorder)break;
				}
				if (!isBorder){
					newFaces.add(fc);
				}
			}
			mesh.faces=newFaces;
		}
	}
	int iC=0;
	public void closeSide(TopoMesh mesh,float x1,float y1,float x2,float y2,float z){
		println("close Side start");
		
		PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(width, height, PDF, "frame-####+"+iC+ ".pdf");
		iC++;
		pdf.beginDraw();
		pdf.fill(255,0,0);
		pdf.noFill();
		pdf.stroke(0);
		pdf.strokeWeight(0.0001f);
		pdf.translate(300,300);
		pdf.scale(200);
		HEGraph heGraphZ1=new HEGraph();
		mesh.rebuildIds();
		for (int i=0;i<mesh.getNNodes();i++){
			TopoMeshNode n=mesh.getNode(i);
			heGraphZ1.addNode(n.x, n.y);
		}
		for (int i=0;i<mesh.getNEdges();i++){
			TopoMeshEdge e=mesh.getEdge(i);
			HENode n1=heGraphZ1.nodes.get(e.getNode1().getId());
			HENode n2=heGraphZ1.nodes.get(e.getNode2().getId());
			heGraphZ1.addHEdge(n1, n2);
		}
		HENode n1=heGraphZ1.addNode(x1,y1);
		HENode n2=heGraphZ1.addNode(x2,y1);
		HENode n3=heGraphZ1.addNode(x2, y2);
		HENode n4=heGraphZ1.addNode(x1, y2);
		
		
		heGraphZ1.addHEdge(n1, n2);
		heGraphZ1.addHEdge(n2, n3);
		heGraphZ1.addHEdge(n3, n4);
		heGraphZ1.addHEdge(n4, n1);
		
		Rect r=new Rect(x1,y1,x2,y2);
		r.x1-=0.001;
		r.y1-=0.001;
		r.x2+=0.001;
		r.y2+=0.001;
		println("---------------start intersect");
		heGraphZ1.fuseCloseNodes2(0.000001f);
		heGraphZ1.findduplicateEdges();
		heGraphZ1.findPointsOnSegments(0.0000001f);
		heGraphZ1.intersectPure();
		//
		println("---------------intersected");
		 Vector<HEdge> newHedges=new  Vector<HEdge>();
		for (int i=0;i<heGraphZ1.hEdges.size();i++){
			HEdge hE=heGraphZ1.hEdges.get(i);
			hE.detach();
			if (r.contains(hE.x1(), hE.y1())&&r.contains(hE.x2(), hE.y2())){
				newHedges.add(hE);
			}
		}
		for (int i=0;i<newHedges.size();i++){
			HEdge hE=newHedges.get(i);
			hE.attach();
		}
		heGraphZ1.hEdges=newHedges;
		heGraphZ1.createHEFacesPur();
		
		println("faces created");
		heGraphZ1.removeEmptyNodes();
		heGraphZ1.rebuildIds();
		mesh.nodes.clear();
		for (int i=0;i<heGraphZ1.getNumVertices();i++){
			HENode n=heGraphZ1.nodes.get(i);
			mesh.addNode(n.x, n.y, z);
		}
		for (int i=0;i<heGraphZ1.heFaces.size();i++){
			HEFace face=heGraphZ1.heFaces.get(i);
			HEdge hE=face.getHEdge();
			TopoMeshFace f=new TopoMeshFace();
			do{
				HENode n=hE.getOrg();
				f.addNode(mesh.nodes.get(n.id));
				hE=hE.getNext();
			}while(hE!=face.getHEdge());
			mesh.addFace(f);
		}
		for (int i=0;i<heGraphZ1.hEdges.size();i++){
			heGraphZ1.hEdges.get(i).display2D(pdf);
		}
		pdf.dispose();
		pdf.endDraw();
		println("close Side End");
	}
	
	public void keyPressed() {
		if (key == 'e')
			doExport = true;
		if (key == 'c')
			doClip = true;
	}
}
